package com.blockchain.securewallet.tools;

import com.blockchain.securewallet.btc.BTCUtils;
import com.blockchain.securewallet.utils.AddressValidity;
import com.blockchain.securewallet.utils.Logger;

import org.apache.commons.codec.digest.DigestUtils;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.regex.Pattern;

public class AddressCheck {

    public static String find_address(String str) {
        if (str == null || str == "") {
            return null;
        }
        int min_len = 31,
                max_len = 46,
                str_len = str.length();
        if (str_len < min_len) {
            return null;
        }
        for (int cur_len = min_len; cur_len <= max_len; cur_len++) {
            if (cur_len > str_len) {
                break;
            }
            for (int index = 0; index < str_len - cur_len; index++) {
                String data = str.substring(index, index + cur_len);
                if (Pattern.compile("^[0-9a-zA-Z]*$").matcher(data).matches()) {
                    data = AddressCheck.get_valid_address(data);
                    if (data != null) {
                        return data;
                    }
                }
            }
        }
        return null;
    }

    protected static String get_valid_address(String data) {
        String addr = AddressCheck.valid_checksum(data);
        if (addr != null) {
            return addr;
        }
        addr = AddressCheck.valid_iban(data);
        if (addr != null) {
            return addr;
        }
        addr = AddressCheck.valid_base58(data);
        if (addr != null) {
            return addr;
        }
        return null;
    }

    protected static String valid_checksum(String data) {
        if (!data.startsWith("0x")) {
            data = "0x" + data;
        }
        if (Pattern.compile("^(0x)?[0-9a-f]{40}$").matcher(data).matches() || Pattern.compile("^(0x)?[0-9A-F]{40}$").matcher(data).matches()) {
            return data;
        }

        MessageDigest md = DigestUtils.getSha256Digest();
        md.update(data.substring(2).getBytes());
        String addr = BTCUtils.toHex(md.digest());
        if (addr.length()<40){
            return null;
        }
        StringBuilder case_map = new StringBuilder();
        Logger.ds("addr: "+ addr);
        if (addr.length() < 40) {
            return null;
        }
        for (int i = 0; i < addr.length(); i++) {
            case_map.append(AddressCheck.base_convert(addr.substring(i, i + 1), 16, 2));
        }

        String map = case_map.toString().substring(0, 40);
        for (int i = 0; i < 40; i++) {
            String chr = map.substring(i, i + 1);
            if ((chr.equals("1") && !chr.toUpperCase().equals(chr)) || (chr.equals("0") && !chr.toLowerCase().equals(chr))) {
                return null;
            }
        }
        return data;
    }

    protected static String valid_base58(String data) {
        // FIXME : btc 校验复制到此
        boolean validity = AddressValidity.btcAddressValidity(data);
        if (validity) {
            return data;
        }
        return null;
    }

    protected static String valid_iban(String data) {
        String token = data.substring(4) + data.substring(0, 4);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < token.length(); i++) {
            int ch = (int) token.charAt(i);
            sb.append(String.valueOf(ch - (ch >= 'A' && ch <= 'Z' ? 55 : 0)));
        }
        if (Pattern.compile("^[0-9]*$").matcher(sb.toString()).matches()) {
            BigInteger result[] = new BigInteger(sb.toString()).divideAndRemainder(new BigInteger("97"));
            if (result[1].intValue() == 1) {
                return AddressCheck.base_convert(data.substring(4), 36, 16);
            }
        }
        return null;
    }

    // 进制转换
    protected static String base_convert(String num, int from_base, int to_base) {
        BigInteger bi = new BigInteger(num, from_base);
        return AddressCheck.to_base_string(to_base, bi, null);
    }

    protected static String to_base_string(int base, BigInteger num, StringBuilder sb) {
        if (sb == null) {
            sb = new StringBuilder();
        }
        BigInteger result[] = num.divideAndRemainder(new BigInteger(String.valueOf(base)));
        if (result[1].compareTo(new BigInteger("36")) != -1) {
            sb.insert(0, (char) (result[1].intValue() - 36 + 'A'));
        } else if (result[1].compareTo(new BigInteger("10")) != -1) {
            sb.insert(0, (char) (result[1].intValue() - 10 + 'a'));
        } else {
            sb.insert(0, String.valueOf(result[1].intValue()));
        }
        if (result[0].compareTo(new BigInteger("0")) == 0) {
            return sb.toString();
        }
        return AddressCheck.to_base_string(base, result[0], sb);
    }

    public static void test(String args[]) {
        String[] addrs = {
                "iban:XE79FHFW4TLRP312QU8PN8AYSW7VYDXFIMN?amount=123&token=ETH",
                "bitcoin:1iPzFMFNaJX1ZC1ZKRot4tiLzDcoRm2mq?amount=666",
                "ethereum:0x279349978f2ed9f1dfacf8344ddbbf09001bb99a?amount=69",
                "iban:XE79FHFW4TLRP312QU8PN8AYSW7VYDXFIMN?amount=0&token=ANT",
                "bitcoinj1iPzFMFNaJX1ZC1ZKRot4tiLzDcoRm2mqiwnamounti666",
                "ethereum20x279349978f2ed9f1dfacf8344ddbbf09001bb99a9dwdamounta69",
                "ibanlXE79FHFW4TLRP312QU8PN8AYSW7VYDXFIMNlkjwdamount000tokenaANT"
        };
        for (int i = 0; i < addrs.length; i++) {
            System.out.println("origin : " + addrs[i]);
            System.out.println("find   : " + AddressCheck.find_address(addrs[i]) + "\n");
        }
    }

}
